//
// Created by syj on 2023/10/10.
//

#ifndef HELLO_DDS_DDSMESSAGEPUBSUBTYPES_H
#define HELLO_DDS_DDSMESSAGEPUBSUBTYPES_H

#include <fastcdr/cdr/fixed_size_string.hpp>
#include <fastcdr/xcdr/optional.hpp>

#include <fastdds/dds/core/policy/QosPolicies.hpp>
#include <fastdds/dds/topic/TopicDataType.hpp>
#include <fastdds/rtps/common/InstanceHandle.h>
#include <fastdds/rtps/common/SerializedPayload.h>
#include <fastrtps/utils/md5.h>

#include <fastcdr/FastBuffer.h>
#include <fastcdr/Cdr.h>
#include <fastcdr/CdrSizeCalculator.hpp>

#if defined(_WIN32)
#if defined(EPROSIMA_USER_DLL_EXPORT)
#define eProsima_user_DllExport __declspec( dllexport )
#else
#define eProsima_user_DllExport
#endif  // EPROSIMA_USER_DLL_EXPORT
#else
#define eProsima_user_DllExport
#endif  // _WIN32

#if defined(_WIN32)
#if defined(EPROSIMA_USER_DLL_EXPORT)
#if defined(HELLOWORLD_SOURCE)
#define HELLOWORLD_DllAPI __declspec( dllexport )
#else
#define HELLOWORLD_DllAPI __declspec( dllimport )
#endif // HELLOWORLD_SOURCE
#else
#define HELLOWORLD_DllAPI
#endif  // EPROSIMA_USER_DLL_EXPORT
#else
#define HELLOWORLD_DllAPI
#endif // _WIN32

constexpr uint32_t HelloWorld_max_cdr_typesize {268UL};
constexpr uint32_t HelloWorld_max_key_cdr_typesize {0UL};


namespace eprosima {
    namespace fastcdr {

        class Cdr;
        class CdrSizeCalculator;

        template <typename MessageT>
        eProsima_user_DllExport size_t calculate_serialized_size(
                eprosima::fastcdr::CdrSizeCalculator& calculator,
                const MessageT& data,
                size_t& current_alignment)
        {
            static_cast<void>(data);

            eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding();
            size_t calculated_size {calculator.begin_calculate_type_serialized_size(
                    eprosima::fastcdr::CdrVersion::XCDRv2 == calculator.get_cdr_version() ?
                    eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 :
                    eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR,
                    current_alignment)};


//            calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0),data.index(), current_alignment);

//            calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1),data.message(), current_alignment);

            calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment);

            return calculated_size;
        }

        template <typename MessageT>
        eProsima_user_DllExport void serialize(
                eprosima::fastcdr::Cdr& scdr,
                const MessageT& data)
        {
            eprosima::fastcdr::Cdr::state current_state(scdr);
            scdr.begin_serialize_type(current_state,
                                      eprosima::fastcdr::CdrVersion::XCDRv2 == scdr.get_cdr_version() ?
                                      eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 :
                                      eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR);

//            scdr << eprosima::fastcdr::MemberId(0) << data.index() << eprosima::fastcdr::MemberId(1) << data.message();

            scdr.end_serialize_type(current_state);
        }

        template <typename MessageT>
        eProsima_user_DllExport void deserialize(
                eprosima::fastcdr::Cdr& cdr,
                MessageT& data)
        {
            cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ?
                                 eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 :
                                 eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR,
                                 [&data](eprosima::fastcdr::Cdr& dcdr, const eprosima::fastcdr::MemberId& mid) -> bool
                                 {
                                     bool ret_value = true;
                                     switch (mid.id)
                                     {
                                         case 0:
//                                             dcdr >> data.index();
                                             break;

                                         case 1:
//                                             dcdr >> data.message();
                                             break;

                                         default:
                                             ret_value = false;
                                             break;
                                     }
                                     return ret_value;
                                 });
        }

        template <typename MessageT>
        eProsima_user_DllExport void serialize_key(
                eprosima::fastcdr::Cdr& scdr,
                const MessageT& data)
        {
            static_cast<void>(scdr);
            static_cast<void>(data);
        }

    } // namespace fastcdr
} // namespace eprosima

enum class DDSTransportType
{
    DEFAULT=0,
    SHM,
    UDPv4,
    UDPv6,
};

#if !defined(GEN_API_VER) || (GEN_API_VER != 2)
#error \
    Generated HelloWorld is not compatible with current installed Fast DDS. Please, regenerate it with fastddsgen.
#endif  // GEN_API_VER


/*!
 * @brief This class represents the TopicDataType of the type HelloWorld defined by the user in the IDL file.
 * @ingroup HelloWorld
 */
template <typename MessageT>
class MessagePubSubType : public eprosima::fastdds::dds::TopicDataType
{
    using SerializedPayload_t = eprosima::fastrtps::rtps::SerializedPayload_t;
    using InstanceHandle_t = eprosima::fastrtps::rtps::InstanceHandle_t;
    using DataRepresentationId_t = eprosima::fastdds::dds::DataRepresentationId_t;
public:
    typedef MessageT type;

    eProsima_user_DllExport MessagePubSubType()
    {
        setName("Motovis");
        uint32_t type_size = HelloWorld_max_cdr_typesize;
        type_size += static_cast<uint32_t>(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */
        m_typeSize = type_size + 4; /*encapsulation*/
        m_isGetKeyDefined = false;
        uint32_t keyLength = HelloWorld_max_key_cdr_typesize > 16 ? HelloWorld_max_key_cdr_typesize : 16;
        m_keyBuffer = reinterpret_cast<unsigned char*>(malloc(keyLength));
        memset(m_keyBuffer, 0, keyLength);
    }

    virtual eProsima_user_DllExport ~MessagePubSubType() override
    {
        if (m_keyBuffer != nullptr)
        {
            free(m_keyBuffer);
        }
    }

    eProsima_user_DllExport bool serialize(
            void* data,
            eprosima::fastrtps::rtps::SerializedPayload_t* payload) override
    {
        return serialize(data, payload, eprosima::fastdds::dds::DEFAULT_DATA_REPRESENTATION);
    }

    eProsima_user_DllExport bool serialize(
            void* data,
            eprosima::fastrtps::rtps::SerializedPayload_t* payload,
            eprosima::fastdds::dds::DataRepresentationId_t data_representation) override
    {
        MessageT* p_type = static_cast<MessageT*>(data);

        // Object that manages the raw buffer.
        eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast<char*>(payload->data), payload->max_size);
        // Object that serializes the data.
        eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN,
                                   data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ?
                                   eprosima::fastcdr::CdrVersion::XCDRv1 : eprosima::fastcdr::CdrVersion::XCDRv2);
        payload->encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE;
        ser.set_encoding_flag(
                data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ?
                eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR  :
                eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2);

        try
        {
            // Serialize encapsulation
            ser.serialize_encapsulation();
            // Serialize the object.
            ser << *p_type;
        }
        catch (eprosima::fastcdr::exception::Exception& /*exception*/)
        {
            return false;
        }

        // Get the serialized length
        payload->length = static_cast<uint32_t>(ser.get_serialized_data_length());
        return true;
    }

    eProsima_user_DllExport bool deserialize(
            eprosima::fastrtps::rtps::SerializedPayload_t* payload,
            void* data) override

    {
        try
        {
            // Convert DATA to pointer of your type
            MessageT* p_type = static_cast<MessageT*>(data);

            // Object that manages the raw buffer.
            eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast<char*>(payload->data), payload->length);

            // Object that deserializes the data.
            eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN);

            // Deserialize encapsulation.
            deser.read_encapsulation();
            payload->encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE;

            // Deserialize the object.
            deser >> *p_type;
        }
        catch (eprosima::fastcdr::exception::Exception& /*exception*/)
        {
            return false;
        }

        return true;
    }

    eProsima_user_DllExport std::function<uint32_t()> getSerializedSizeProvider(
            void* data) override
    {
        return getSerializedSizeProvider(data, eprosima::fastdds::dds::DEFAULT_DATA_REPRESENTATION);
    }

    eProsima_user_DllExport std::function<uint32_t()> getSerializedSizeProvider(
            void* data,
            eprosima::fastdds::dds::DataRepresentationId_t data_representation) override

    {
        return [data, data_representation]() -> uint32_t
        {
            eprosima::fastcdr::CdrSizeCalculator calculator(
                    data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ?
                    eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2);
            size_t current_alignment {0};
            return static_cast<uint32_t>(calculator.calculate_serialized_size(
                    *static_cast<MessageT*>(data), current_alignment)) +
                   4u /*encapsulation*/;
        };
    }

    eProsima_user_DllExport bool getKey(
            void* data,
            eprosima::fastrtps::rtps::InstanceHandle_t* handle,
            bool force_md5 = false) override

    {
        if (!m_isGetKeyDefined)
        {
            return false;
        }

        MessageT* p_type = static_cast<MessageT*>(data);

        // Object that manages the raw buffer.
        eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast<char*>(m_keyBuffer),
                                                 HelloWorld_max_key_cdr_typesize);

        // Object that serializes the data.
        eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::BIG_ENDIANNESS);
        eprosima::fastcdr::serialize_key(ser, *p_type);
        if (force_md5 || HelloWorld_max_key_cdr_typesize > 16)
        {
            m_md5.init();
            m_md5.update(m_keyBuffer, static_cast<unsigned int>(ser.get_serialized_data_length()));
            m_md5.finalize();
            for (uint8_t i = 0; i < 16; ++i)
            {
                handle->value[i] = m_md5.digest[i];
            }
        }
        else
        {
            for (uint8_t i = 0; i < 16; ++i)
            {
                handle->value[i] = m_keyBuffer[i];
            }
        }
        return true;
    }

    eProsima_user_DllExport void* createData() override

    {
        return reinterpret_cast<void*>(new MessageT());
    }

    eProsima_user_DllExport void deleteData(
            void* data) override

    {
        delete(reinterpret_cast<MessageT*>(data));
    }

#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED
    eProsima_user_DllExport inline bool is_bounded() const override
    {
        return false;
    }

#endif  // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED

#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN
    eProsima_user_DllExport inline bool is_plain() const override
    {
        return false;
    }

    eProsima_user_DllExport inline bool is_plain(
            eprosima::fastdds::dds::DataRepresentationId_t data_representation) const override
    {
        static_cast<void>(data_representation);
        return false;
    }

#endif  // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN

#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE
    eProsima_user_DllExport inline bool construct_sample(
            void* memory) const override
    {
        static_cast<void>(memory);
        return false;
    }

#endif  // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE

    MD5 m_md5;
    unsigned char* m_keyBuffer;

};

#endif //HELLO_DDS_DDSMESSAGEPUBSUBTYPES_H
